package com.tianji.learning.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.db.PageResult;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.api.client.course.CatalogueClient;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.course.CataSimpleInfoDTO;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.course.CourseSearchDTO;
import com.tianji.api.dto.course.CourseSimpleInfoDTO;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.domain.query.PageQuery;
import com.tianji.common.exceptions.BadRequestException;
import com.tianji.common.utils.*;
import com.tianji.learning.domain.dto.LearningPlanDTO;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.domain.vo.LearningLessonVO;
import com.tianji.learning.domain.vo.LearningPlanPageVO;
import com.tianji.learning.domain.vo.LearningPlanVO;
import com.tianji.learning.enums.LessonStatus;
import com.tianji.learning.enums.PlanStatus;
import com.tianji.learning.listener.LessonListener;
import com.tianji.learning.mapper.LearningLessonMapper;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.service.ILearningLessonService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.tianji.learning.enums.LessonStatus.*;

/**
 * <p>
 * 学生课程表 服务实现类
 * </p>
 *
 * @author 丁真
 * @since 2025-06-27
 */
@Service
@RequiredArgsConstructor
public class LearningLessonServiceImpl extends ServiceImpl<LearningLessonMapper, LearningLesson> implements ILearningLessonService {

    private final CourseClient courseClient;

    private final CatalogueClient catalogueClient;
    private final LearningRecordMapper learningRecordMapper;

    /**
     * 添加课程信息到用户课程表
     *
     * @param userId
     * @param courseIds
     */
    @Override
    public void addUserLessons(Long userId, List<Long> courseIds) {
        List<CourseSimpleInfoDTO> simpleInfoList = courseClient.getSimpleInfoList(courseIds);
        // 1.查询课程有效期
        if (CollUtils.isEmpty(simpleInfoList)) {
            log.error("课程信息不存在，无法添加到课表");
            return;
        }
        // 2.循环遍历，处理LearningLesson数据
        List<LearningLesson> list = new ArrayList<>(simpleInfoList.size());
        for (CourseSimpleInfoDTO cInfo : simpleInfoList) {
            LearningLesson lesson = new LearningLesson();
            Integer validDuration = cInfo.getValidDuration();
            if (validDuration == null || validDuration <= 0) {
                continue;
            }
            if (validDuration < 9999) {
                lesson.setCreateTime(LocalDateTime.now());
                lesson.setExpireTime(LocalDateTime.now().plusMonths(validDuration));
            }
            lesson.setUserId(userId);
            lesson.setCourseId(cInfo.getId());
            list.add(lesson);
        }
        // 3、批量插入课表数据
        this.saveBatch(list);

    }

    /**
     * 分页查询我的课表
     *
     * @param pageQuery
     * @return
     */
    @Override
    public PageDTO<LearningLessonVO> pageLessons(PageQuery pageQuery) {
        // 1.获取当前登录用户
        Long userId = UserContext.getUser();

        // 2.分页查询
        // select * from learning_lesson where user_id = #{userId} order by latest_learn_time limit 0, 5
        Page<LearningLesson> page = page(
                pageQuery.toMpPage("latest_learn_time", false),
                Wrappers.<LearningLesson>lambdaQuery().eq(LearningLesson::getUserId, userId)
        );
        List<LearningLesson> records = page.getRecords();
        if (CollUtils.isEmpty(records)) {
            return PageDTO.empty(page);
        }

        // 3、发起远程调用，查询课程详细信息(课程名称、封面、总章节数)
        Map<Long, CourseSimpleInfoDTO> cMap = queryCourseSimpleInfoList(records);


        // 4、循环分页结果，将poList 转为 voList
        List<LearningLessonVO> lessonVOList = new ArrayList<>(records.size());
        for (LearningLesson record : records) {
            LearningLessonVO lessonVO = BeanUtils.toBean(record, LearningLessonVO.class);
            CourseSimpleInfoDTO cInfo = cMap.get(record.getCourseId());
            lessonVO.setCourseName(cInfo.getName());
            lessonVO.setCourseCoverUrl(cInfo.getCoverUrl());
            lessonVO.setSections(cInfo.getSectionNum());
            lessonVOList.add(lessonVO);

        }

        // 5、返回分页结果

        return PageDTO.of(page, lessonVOList);
    }

    /**
     * 查询我最近正在学习的课程
     *
     * @return
     */
    @Override
    public LearningLessonVO now() {
        // 1.获取用户id
        Long userId = UserContext.getUser();
        // 2.查询正在学习的课程
        // select * from xx where user_id = #{userId} AND status = 1 order by latest_learn_time limit 1
        LearningLesson lesson = lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getStatus, LEARNING)
                .orderByDesc(LearningLesson::getLatestLearnTime)
                .last("limit 1")
                .one();

        // 3、拷贝属性
        LearningLessonVO lessonVO = BeanUtils.toBean(lesson, LearningLessonVO.class);

        // 4、远程调用查询课程信息(名称、封面、小节总数)
        CourseFullInfoDTO courseInfo = courseClient.getCourseInfoById(lesson.getCourseId(), false, false);
        if (ObjectUtil.isEmpty(courseInfo)) {
            log.error("课程信息不存在！");
            return new LearningLessonVO();
        }

        // 5、统计课表中该用户的课表数量
        // select count(1) from learning_lesson where user_id = #{userId}
        Integer count = this.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .count();

        // 6、查询最近学习的小节信息(名称、序号)
        List<CataSimpleInfoDTO> cataInfos = catalogueClient.batchQueryCatalogue(CollUtils.singletonList(lesson.getLatestSectionId()));

        // 7、补充赋值
        if (!CollUtils.isEmpty(cataInfos)) {
            CataSimpleInfoDTO cataInfo = cataInfos.get(0);
            lessonVO.setLatestSectionName(cataInfo.getName());
            lessonVO.setLatestSectionIndex(cataInfo.getCIndex());
        }
        lessonVO.setCourseName(courseInfo.getName());
        lessonVO.setCourseCoverUrl(courseInfo.getCoverUrl());
        lessonVO.setSections(courseInfo.getSectionNum());
        lessonVO.setCourseAmount(count);


        // 8、返回
        return lessonVO;
    }

    /**
     * 创建学习计划
     *
     * @param planDTO
     */
    @Override
    public void savePlans(LearningPlanDTO planDTO) {
        // 1、查询课表信息
        LearningLesson lesson = getOne(
                Wrappers.<LearningLesson>lambdaQuery()
                        .eq(LearningLesson::getUserId, UserContext.getUser())
                        .eq(LearningLesson::getCourseId, planDTO.getCourseId())
        );
        AssertUtils.isNotNull(lesson, "课程不存在，无法创建学习计划");

        // 2、更新学习计划数据
        boolean update = this.lambdaUpdate()
                .set(LearningLesson::getWeekFreq, planDTO.getFreq())
                .set(lesson.getPlanStatus() == PlanStatus.NO_PLAN, LearningLesson::getPlanStatus, PlanStatus.PLAN_RUNNING)
                .eq(LearningLesson::getId, lesson.getId())
                .update();
        AssertUtils.isTrue(update, "更新数据失败！");

    }

    /**
     * 统计查询我的学习计划
     *
     * @param pageQuery
     * @return
     */
    @Override
    public LearningPlanPageVO pagePlans(PageQuery pageQuery) {
        LearningPlanPageVO pageVO = new LearningPlanPageVO();
        // 1、获取当前登录人ID
        Long userId = UserContext.getUser();

        // 2、分页查询我的课表（我的、制定过学习计划、课表状态不为失效的
        // SELECT * FROM learning_lesson WHERE user_id = 2 AND plan_status = 1 AND `status` IN (0,1,2) limit ?,?
        Page<LearningLesson> page = this.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getPlanStatus, PlanStatus.PLAN_RUNNING)
                .in(LearningLesson::getStatus, LEARNING, NOT_BEGIN, FINISHED)
                .page(pageQuery.toMpPage("latest_learn_time", false));
        List<LearningLesson> records = page.getRecords();

        if (ObjectUtil.isEmpty(records)) {
            return pageVO.pageInfo(page);
        }

        // 3、收集课程ID，根据课程ID集合远程调用查询课程信息
        Map<Long, CourseSimpleInfoDTO> map = queryCourseSimpleInfoList(records);

        // 4、统计本周实际学习小节数量
        // select lesson_id,count(1) num from `learning_record` where user_id = 2 and finished = 1
        // and finish_time between '2025-06-23 00:00:00' and '2025-06-29 23:59:59' GROUP BY lesson_id
        // 4.1、获取本周开始、结束日期
        LocalDateTime beginTime = DateUtils.getWeekBeginTime(LocalDate.now());
        LocalDateTime endTime = DateUtils.getWeekEndTime(LocalDate.now());

        // 4.2、发起分组统计
        QueryWrapper<LearningRecord> wrapper = Wrappers.<LearningRecord>query()
                .eq("user_id", userId)
                .eq("finished", true)
                .between("finish_time", beginTime, endTime)
                .select("lesson_id", "count(1) as num")
                .groupBy("lesson_id");
        List<Map<String, Object>> mapList = learningRecordMapper.selectMaps(wrapper);
        Map<Long, Long> lessonIdWithNumMap = mapList.stream()
                .collect(Collectors.toMap(key -> (Long) key.get("lesson_id"), value -> (Long) value.get("num")));

        // 5、循环分页课表数据，封装填充缺失数据
        List<LearningPlanVO> planVOS = new ArrayList<>();
        for (LearningLesson record : records) {
            // 5.1、拷贝数据
            LearningPlanVO planVO = BeanUtils.toBean(record, LearningPlanVO.class);

            // 5.2、填充课程数据
            CourseSimpleInfoDTO courseSimpleInfoDTO = map.get(record.getCourseId());
            if (ObjectUtil.isNotEmpty(courseSimpleInfoDTO)) {
                planVO.setCourseName(courseSimpleInfoDTO.getName());
                planVO.setSections(courseSimpleInfoDTO.getSectionNum());
            }

            // 5.3、填充本周已学习小节数
            planVO.setWeekLearnedSections(lessonIdWithNumMap.getOrDefault(record.getId(), 0L).intValue());

            // 5.4、添加到集合
            planVOS.add(planVO);

        }

        // 6、计算 本周已学习小节总数量
        int sum = lessonIdWithNumMap.values().stream().mapToInt(Long::intValue).sum();

        // 7、计算 本周计划学习的总小节数
        // SELECT sum(week_freq) total_weekfreq FROM learning_lesson WHERE user_id = 2 AND plan_status = 1 AND `status` IN (0,1,2)
        LearningLesson lesson = this.query()
                .select("sum(week_freq) as total_weekfreq")
                .eq("user_id", userId)
                .eq("plan_status", true)
                .in("status", LEARNING, NOT_BEGIN, FINISHED)
                .one();

        // 8、封装数据并返回
        pageVO.setWeekFinished(sum);
        pageVO.setWeekTotalPlan(lesson.getTotalWeekfreq());
        pageVO.pageInfo(page.getTotal(), page.getPages(), planVOS);
        return pageVO;
    }

    /**
     * 订单取消,批量删除我的课表
     *
     * @param userId
     * @param courseIds
     */
    @Override
    public void removeUserLessons(Long userId, List<Long> courseIds) {
        if (userId == null) {
            userId = UserContext.getUser();
        }
        // 2.批量删除
        boolean remove = this.remove(
                Wrappers.<LearningLesson>lambdaQuery()
                        .eq(LearningLesson::getUserId, userId)
                        .in(LearningLesson::getCourseId, courseIds)
        );
        if (!remove) {
            log.error("删除我的课表失败！");
        }
    }

    /**
     * 校验当前用户是否可以学习当前课程
     *
     * @param courseId
     * @return
     */
    @Override
    public Long isLessonValid(Long courseId) {
        //1. 查询用户课表中是否有该课程
        if (courseId == null) {
            log.error("课程ID不能为空！");
            return null;
        }
        Long userId = UserContext.getUser();
        LearningLesson lesson = this.lambdaQuery()
                .eq(LearningLesson::getCourseId, courseId)
                .eq(LearningLesson::getUserId, userId)
                .one();
        if (lesson == null){
            log.error("课程信息不存在！");
            return null;
        }

        //2. 判断课程状态是否是有效的状态（未过期）
        if (lesson.getStatus().equals(EXPIRED)) {
            log.error("课程已过期！");
            return null;
        }

        return lesson.getCourseId();
    }

    /**
     * 查询用户课表中指定课程状态
     *
     * @param courseId
     * @return
     */
    @Override
    public LearningLessonVO searchStatus(Long courseId) {
        //1.用户id
        Long userId = UserContext.getUser();

        //2.根据userid和courseId查询课表
        LearningLesson lesson = this.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId, courseId)
                .ne(LearningLesson::getStatus, EXPIRED)
                .one();
        AssertUtils.isNotNull(lesson, "课程信息不存在！");

        return BeanUtils.toBean(lesson, LearningLessonVO.class);
    }

    /**
     * 统计课程学习人数
     *
     * @param courseId
     * @return
     */
    @Override
    public Integer countLearningLessonByCourse(Long courseId) {
        //根据courseId统计课程学习人数
        return this.lambdaQuery()
                .eq(LearningLesson::getCourseId, courseId)
                .ne(LearningLesson::getStatus, EXPIRED)
                .count();
    }

    /**
     * 查询课程信息
     *
     * @param records
     * @return
     */
    private Map<Long, CourseSimpleInfoDTO> queryCourseSimpleInfoList(List<LearningLesson> records) {
        // Set<Long> collect = records.stream().map(LearningLesson::getCourseId).collect(Collectors.toSet());
        Set<Long> courseIds = CollStreamUtil.toSet(records, LearningLesson::getCourseId);
        List<CourseSimpleInfoDTO> cInfos = courseClient.getSimpleInfoList(courseIds);
        if (CollUtils.isEmpty(cInfos)) {
            throw new BadRequestException("课程信息不存在！");
        }
        return CollStreamUtil.toMap(cInfos, c -> c.getId(), Function.identity());
    }


}
